package com.evaluation_system.service.impl;

import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.auth.EnvironmentVariableCredentialsProvider;
import com.evaluation_system.Util.PdfSM4Encryptor;
import com.evaluation_system.exception.commitChainFail;
import com.evaluation_system.exception.fileEncryptFailException;
import com.evaluation_system.exception.fileSaveFailException;
import com.evaluation_system.mapper.UploaderMapper;
import com.evaluation_system.pojo.entity.*;
import com.evaluation_system.pojo.DTO.FilePageQueryDTO;
import com.evaluation_system.pojo.DTO.UploaderPageQueryDTO;
import com.evaluation_system.pojo.Result.PageResult;
import com.evaluation_system.pojo.entity.saveFileInfo;
import com.evaluation_system.service.UploaderService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.transaction.manager.AssembleTransactionProcessor;
import org.fisco.bcos.sdk.transaction.manager.TransactionProcessorFactory;
import org.fisco.bcos.sdk.transaction.model.dto.TransactionResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;

//aliYun
import com.aliyun.oss.*;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.model.*;

import java.net.URL;


@Service

@RequiredArgsConstructor
@Slf4j
public class UploaderServiceImpl implements UploaderService {

    @Value("${oss.bucket-name}")
    private String bucketName;

    @Autowired
    private UploaderMapper uploadermapper;

    private byte[] encryptedBytes;
    private abstractInfo abstractInformation;
    SecretKey secretKey;
    private IvParameterSpec iv;
    Integer id;


    @Override
    public Integer getUserId(String username) {
        return uploadermapper.getId(username);
    }


    @Override
    public Map<String, Object>  encryptInfo(MultipartFile file, String filename) {
        Map<String, Object> response=new HashMap<>();



        try {
            // 1. 基础校验
            if (file.isEmpty()) {
                throw new IllegalArgumentException("文件不能为空");
            }
            if (!"application/pdf".equals(file.getContentType())) {
                throw new IllegalArgumentException("仅支持PDF文件");
            }

            // 2. 生成密钥和IV
            secretKey = PdfSM4Encryptor.generateKey();
            iv = PdfSM4Encryptor.generateIv();

            // 3. 加密文件
            encryptedBytes = PdfSM4Encryptor.encrypt(file.getBytes(), secretKey, iv);

            // 4. 保存加密文件（实际项目应存到安全存储）
            String safeFilename = filename + "enc.pdf";
            Path outputPath = Paths.get("encrypted_files", safeFilename);
            Files.createDirectories(outputPath.getParent());
            Files.write(outputPath, encryptedBytes);

//            / ===== 新增的解密逻辑 =====
            // 5. 立即解密验证（使用刚才生成的密钥和IV）
            byte[] decryptedBytes = PdfSM4Encryptor.decrypt(encryptedBytes, secretKey, iv);

            log.info(" iv is " + iv.toString());

            // 6. 保存解密后的PDF
            Path decryptedPath = Paths.get("encrypted_files", filename+"20250804解密.pdf");
            Files.write(decryptedPath, decryptedBytes);

            // 5. 返回加密元数据（密钥需安全存储！）
            response.put("status", "success");
            response.put("encryptedFile", outputPath.toString());
            response.put("key", Base64.getEncoder().encodeToString(secretKey.getEncoded()));
            response.put("iv", Base64.getEncoder().encodeToString(iv.getIV()));



        } catch (Exception e) {
            response.put("status", "error");
            response.put("message", "加密失败: " + e.getMessage());
            throw new fileEncryptFailException("文件加密失败: " + e.getMessage());

        }
        return response;
    }

    @Override
    public void saveFile(UploaderFile uploaderFile) {
        try {
            String fileName = uploaderFile.getFileName();
            String summary = uploaderFile.getSummary();
            id=uploaderFile.getUserId();
            uploadermapper.insertFile(new saveFileInfo(id,summary,this.saveAliyun(this.encryptedBytes,id,fileName),fileName,Base64.getEncoder().encodeToString(this.secretKey.getEncoded()),Base64.getEncoder().encodeToString(this.iv.getIV())));
        } catch (Exception e) {
            throw new fileSaveFailException("文件保存失败: " + e.getMessage());
        }
    }
//        uploadmapper.insertFile(this.id, summary, this.filePath,fileName, Base64.getEncoder().encodeToString(this.secretKey.getEncoded()));

//    调用该函数之前请启用阿里云oss服务，需要配置阿里云的密钥，创建bucket
    @Override
    public String saveAliyun(byte[] fileBytes,Integer id,String fileName) {
        try  {
            // Endpoint以华东1（杭州）为例，其它Region请按实际情况填写。
            String endpoint = "https://oss-cn-beijing.aliyuncs.com";
            // 从环境变量中获取访问凭证。运行本代码示例之前，请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
//            // 填写Bucket名称.
//            String bucketName = "java-aixdy";
            // 填写Object完整路径，完整路径中不能包含Bucket名称，例如exampledir/exampleobject.txt。
            String objectName = id.toString()+"/"+fileName+".pdf";
            // 填写Bucket所在地域。
            String region = "cn-beijing";

            // 创建OSSClient实例。
            // 当OSSClient实例不再使用时，调用shutdown方法以释放资源。
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();

            try {
                byte[] content = this.encryptedBytes;
                // 创建PutObjectRequest对象。
                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, new ByteArrayInputStream(content));

                // 创建PutObject请求。
                PutObjectResult result = ossClient.putObject(putObjectRequest);

                Date expiration = new Date(new Date().getTime() + 3600L * 1000 * 24 * 365 * 10);
                // 生成以GET方法访问的预签名URL。本示例没有额外请求头，其他人可以直接通过浏览器访问相关内容。
                URL url = ossClient.generatePresignedUrl(bucketName, objectName,expiration);
                System.out.println(url.toString().split("\\?")[0]);

                return url.toString().split("\\?")[0];
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    public PageResult<Uploader> pageQuery(UploaderPageQueryDTO uploaderPageQueryDTO) {


            // 开启分页
            PageHelper.startPage(uploaderPageQueryDTO.getPage(), uploaderPageQueryDTO.getPageSize());

            // 执行查询
            Page<Uploader> page = uploadermapper.pageQuery(uploaderPageQueryDTO);

            // 封装结果
            return new PageResult<>(page.getTotal(), page.getResult());

    }

    @Override
    public void add(Uploader uploader) {
        uploadermapper.insert(uploader);
    }

    @Override
    public void update(Uploader uploader) {
        uploadermapper.update(uploader);
    }

    @Override
    public void deleteById(Integer id) {
        uploadermapper.deleteById(id);
    }

    @Override
    public Integer commitBlockChain(Integer fileId, String summary,String scoreId) {
        //        获取配置文件的路径

        try {
//            String configFile = "src/main/resources/config-example.toml";
            // 初始化BcosSDK对象
//            BcosSDK sdk = BcosSDK.build(configFile);
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config-example.toml");
            File tempFile = File.createTempFile("config", ".toml");
            Files.copy(inputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);

            BcosSDK sdk = BcosSDK.build(tempFile.getAbsolutePath());




            // 获取Client对象，此处传入的群组ID为1
            Client client = sdk.getClient(Integer.valueOf(1));
            // 构造AssembleTransactionProcessor对象，需要传入client对象，CryptoKeyPair对象和abi、binary文件存放的路径。abi和binary文件需要在上一步复制到定义的文件夹中。
            CryptoKeyPair keyPair = client.getCryptoSuite().createKeyPair();

            //仅交易和查询
//            AssembleTransactionProcessor transactionProcessor = TransactionProcessorFactory.createAssembleTransactionProcessor(client, keyPair, "src/main/resources/abi/", "");
            String abiDirPath = extractAbiDirToTemp();

            AssembleTransactionProcessor transactionProcessor =
                    TransactionProcessorFactory.createAssembleTransactionProcessor(
                            client,
                            keyPair,
                            abiDirPath,
                            ""
                    );
            //上链过程：

            //上链数据构造
            List<Object> params = new ArrayList<>();
            params.add(fileId.toString());
            params.add(summary);
            params.add(scoreId);

            //调用合约的上链函数
            TransactionResponse transactionResponse = transactionProcessor.sendTransactionAndGetResponseByContractLoader("FileSummary","0x1c8fe7aabe2546a9abe6175c4a1b3ac21c24bd6b","InsertSummary", params);

            // 获取交易哈希
            String transactionHash = transactionResponse.getTransactionReceipt().getTransactionHash();
            System.out.println("Transaction Hash: " + transactionHash);
            //保存hash1

            uploadermapper.saveHash1(fileId,transactionHash);

            //打印返回值
            List<Object> returnValues = transactionResponse.getReturnObject();

            if(returnValues != null){
                for(Object returnValue : returnValues){
                    System.out.println("返回值: "+ returnValue.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new commitChainFail("上链失败");
        }

        return 1;
    }

    @Override
    public Integer getFileId(String fileName) {
        return uploadermapper.getFileID(fileName);
    }


    @Override
    public abstractInfo getAbstract() {
        return null;
    }

    @Override
    public encryptInfo getEncryptInfo() {

        return null;
    }

    @Override
    public PageResult<saveFileInfo> readFile(FilePageQueryDTO filePageQueryDTO) {
        // 分页查询
        PageHelper.startPage(filePageQueryDTO.getPage(), filePageQueryDTO.getPageSize());
        // 执行查询
        Page<saveFileInfo> page = uploadermapper.getFile(filePageQueryDTO);
        return new PageResult<>(page.getTotal(), page.getResult());
    }

    @Override
    public Uploader SelectById(Integer userId) {
        return uploadermapper.SelectById(userId);

    }

    @Override
    public Integer FilenameByUploaderId(String filename) {
        return uploadermapper.FilenameByUploaderId(filename);
    }

    @Override
    public void updateFile(UploaderFile uploaderFile) {
        String fileName = uploaderFile.getFileName();
        String summary = uploaderFile.getSummary();
//        id=uploaderFile.getUserId();
        id=uploaderFile.getUserId();
        uploadermapper.updateFile(new saveFileInfo(id,summary,this.saveAliyun(this.encryptedBytes,id,fileName),fileName,Base64.getEncoder().encodeToString(this.secretKey.getEncoded()),Base64.getEncoder().encodeToString(this.iv.getIV())));
        int fileId=uploadermapper.getFileID(fileName);
        uploadermapper.deleteByfileId(fileId);
    }

    public String extractAbiDirToTemp() throws IOException {
        // 1. 创建临时目录
        Path tempDir = Files.createTempDirectory("abi-temp");

        // 2. 定义 abi 和 bin 文件的文件名（不带路径）
        String[] abiFiles = {
                "FileSummary.abi", "FileCheck.abi", "FileScore.abi", "Table.abi"
        };
        String[] binFiles = {
                "FileSummary.bin", "FileCheck.bin", "FileScore.bin", "Table.bin"
        };

        // 3. 拷贝 abi 文件
        for (String fileName : abiFiles) {
            try (InputStream in = getClass().getClassLoader().getResourceAsStream("abi/" + fileName)) {
                if (in == null) {
                    throw new FileNotFoundException("未找到资源文件：abi/" + fileName);
                }
                Files.copy(in, tempDir.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);
            }
        }

        // 4. 拷贝 bin 文件
        for (String fileName : binFiles) {
            try (InputStream in = getClass().getClassLoader().getResourceAsStream("bin/" + fileName)) {
                if (in == null) {
                    throw new FileNotFoundException("未找到资源文件：bin/" + fileName);
                }
                Files.copy(in, tempDir.resolve(fileName), StandardCopyOption.REPLACE_EXISTING);
            }
        }

        return tempDir.toFile().getAbsolutePath();
    }



}
